/*
 * $Id: creature.cpp 315 2010-03-25 23:57:03Z cmtonkinson@gmail.com $
 *
 * This file is part of the Symphony project <http://code.google.com/p/symphonymud/>
 * Copyright 2005-2010 Chris Tonkinson <cmtonkinson@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "area.h"
#include "creature.h"
#include "commandTable-default.h"
#include "exit.h"
#include "handler.h"
#include "identifiers.h"
#include "object-furniture.h"
#include "room.h"
#include "symphony.h"
#include "world.h"

Creature::Creature( void ):
    _inventory( &Identifiers::shortname ) {
  ID( 0 );
  typeFlags( 0 );
  gender( NEUTRAL );
  mode( 0 );
  room( NULL );
  // stats...
  position( Creature::Position_Standing );
  furniture( NULL );
  level( 0 );
  exp( 0 );
  tnl( 0 );
  hp( 0 );
  maxHp( 0 );
  mana( 0 );
  maxMana( 0 );
  movement( 0 );
  maxMovement( 0 );
  strength( 0 );
  maxStrength( 0 );
  dexterity( 0 );
  maxDexterity( 0 );
  constitution( 0 );
  maxConstitution( 0 );
  intelligence( 0 );
  maxIntelligence( 0 );
  wisdom( 0 );
  maxWisdom( 0 );
  charisma( 0 );
  maxCharisma( 0 );
  hitroll( 0 );
  damroll( 0 );
  saves( 0 );
  ac( 0 );
  bash( 0 );
  slash( 0 );
  pierce( 0 );
  exotic( 0 );
  return;
}

Creature::Creature( const Creature& ref ):
    _identifiers( ref.identifiers() ),
    _inventory( ref.inventory() ),
    _equipment( ref.equipment() ) {
  ID( ref.ID() );
  level( ref.level() );
  typeFlags( ref.typeFlags() );
  gender( ref.gender() );
  mode( ref.mode() );
  room( NULL );
  for ( std::list<Modifier*>::const_iterator it = ref.modifiers().begin(); it != ref.modifiers().end(); ++it ) {
    modifiers().push_back( new Modifier( **it ) );
  }
  position( ref.position() );
  furniture( ref.furniture() );
  // stats...
  level( ref.level() );
  exp( ref.exp() );
  tnl( ref.tnl() );
  hp( ref.hp() );
  maxHp( ref.maxHp() );
  mana( ref.mana() );
  maxMana( ref.maxMana() );
  movement( ref.movement() );
  maxMovement( ref.maxMovement() );
  strength( ref.strength() );
  maxStrength( ref.maxStrength() );
  dexterity( ref.dexterity() );
  maxDexterity( ref.maxDexterity() );
  constitution( ref.constitution() );
  maxConstitution( ref.maxConstitution() );
  intelligence( ref.intelligence() );
  maxIntelligence( ref.maxIntelligence() );
  wisdom( ref.wisdom() );
  maxWisdom( ref.maxWisdom() );
  charisma( ref.charisma() );
  maxCharisma( ref.maxCharisma() );
  hitroll( ref.hitroll() );
  damroll( ref.damroll() );
  saves( ref.saves() );
  ac( ref.ac() );
  bash( ref.bash() );
  slash( ref.slash() );
  pierce( ref.pierce() );
  exotic( ref.exotic() );
  return;
}

Creature::~Creature( void ) {
  for ( std::vector<Handler*>::iterator it = handlers().begin(); handlers().size(); ) {
    delete *it;
    it = handlers().erase( it );
  }
  return;
}

Handler* Creature::handler( void ) {
  return handlers().back();
}

void Creature::pushHandler( Handler* handler ) {
  handlers().push_back( handler );
  handler->activate();
  return;
}

void Creature::popHandler( void ) {
  handlers().back()->deactivate();
  handlers().pop_back();
  return;
}

void Creature::replaceHandler( Handler* handler ) {
  popHandler();
  pushHandler( handler );
  return;
}

void Creature::handle( void ) {
  handlers().back()->handle();
  return;
}

Creature* Creature::findCreature( const std::string& name ) {
  for ( std::list<Creature*>::iterator it = room()->creatures().begin(); it != room()->creatures().end(); ++it ) {
    if ( (*it)->identifiers().matchesKeyword( name ) && canSee( *it ) == SEE_NAME ) {
      return *it;
    }
  }
  return NULL;
}

Object* Creature::findObject( const std::string& query ) {
  Object* o = NULL;
  if ( ( o = inventory().searchSingleObject( query ) ) == NULL ) {
    if ( ( o = equipment().searchSingleObject( query ) ) == NULL ) {
      if ( ( o = room()->inventory().searchSingleObject( query ) ) == NULL ) {
        return NULL;
      }
    }
  }
  return o;
}

unsigned short Creature::getWearloc( const Object::Wearable& wearable ) const {
  switch ( wearable ) {
    case Object::Wearable_Head:       return WEARLOC_HEAD;
    case Object::Wearable_Ear:        return WEARLOC_EAR_L;
    case Object::Wearable_Face:       return WEARLOC_FACE;
    case Object::Wearable_Neck:       return WEARLOC_NECK;
    case Object::Wearable_Shoulders:  return WEARLOC_SHOULDERS;
    case Object::Wearable_Arms:       return WEARLOC_ARMS;
    case Object::Wearable_Torso:      return WEARLOC_TORSO;
    case Object::Wearable_Forearm:    return WEARLOC_FOREARM_L;
    case Object::Wearable_Wrist:      return WEARLOC_WRIST_L;
    case Object::Wearable_Hands:      return WEARLOC_HANDS;
    case Object::Wearable_Hold:       return WEARLOC_HOLD_L;
    case Object::Wearable_Finger:     return WEARLOC_FINGER_L;
    case Object::Wearable_Waist:      return WEARLOC_WAIST_1;
    case Object::Wearable_Legs:       return WEARLOC_LEGS;
    case Object::Wearable_Knee:       return WEARLOC_KNEE_L;
    case Object::Wearable_Shin:       return WEARLOC_SHIN_L;
    case Object::Wearable_Ankle:      return WEARLOC_ANKLE_L;
    case Object::Wearable_Feet:       return WEARLOC_FEET;
    default:                          return WEARLOC_END;
  }
}

const char* Creature::wearLocName( const unsigned short& wearloc ) {
  switch ( wearloc ) {
    case WEARLOC_HEAD:      return "<{ghead{x>          ";
    case WEARLOC_EAR_L:     return "<{gleft ear{x>      ";
    case WEARLOC_EAR_R:     return "<{gright ear{x>     ";
    case WEARLOC_FACE:      return "<{gface{x>          ";
    case WEARLOC_NECK:      return "<{gneck{x>          ";
    case WEARLOC_SHOULDERS: return "<{gshoulders{x>     ";
    case WEARLOC_ARMS:      return "<{garms{x>          ";
    case WEARLOC_TORSO:     return "<{gtorso{x>         ";
    case WEARLOC_FOREARM_L: return "<{gleft forearm{x>  ";
    case WEARLOC_FOREARM_R: return "<{gright forearm{x> ";
    case WEARLOC_WRIST_L:   return "<{gleft wrist{x>    ";
    case WEARLOC_WRIST_R:   return "<{gright wrist{x>   ";
    case WEARLOC_HANDS:     return "<{ghands{x>         ";
    case WEARLOC_HOLD_L:    return "<{gleft hand{x>     ";
    case WEARLOC_HOLD_R:    return "<{gright hand{x>    ";
    case WEARLOC_FINGER_L:  return "<{gleft finger{x>   ";
    case WEARLOC_FINGER_R:  return "<{gright finger{x>  ";
    case WEARLOC_WAIST_1:   return "<{gwaist{x>         ";
    case WEARLOC_WAIST_2:   return "<{gwaist{x>         ";
    case WEARLOC_LEGS:      return "<{glegs{x>          ";
    case WEARLOC_KNEE_L:    return "<{gleft knee{x>     ";
    case WEARLOC_KNEE_R:    return "<{gright knee{x>    ";
    case WEARLOC_SHIN_L:    return "<{gleft shin{x>     ";
    case WEARLOC_SHIN_R:    return "<{gright shin{x>    ";
    case WEARLOC_ANKLE_L:   return "<{gleft ankle{x>    ";
    case WEARLOC_ANKLE_R:   return "<{gright ankle{x>   ";
    case WEARLOC_FEET:      return "<{gfeet{x>          ";
    default:                return "[{Rerror{x]       ";
  }
}

void Creature::room( Room* room ) {
  CmdExit exit;
  std::vector<std::string> exit_args;
  _room = room;
  if ( mode() == MODE_REDIT ) {
    if ( !World::Instance().hasPermission( room->area(), (Avatar*)this ) ) {
      while ( mode() != MODE_NONE ) {
        exit.execute( this, exit_args );
      }
      return;
    }
    // Make sure no one else is editing the room...
    for ( std::map<std::string,Avatar*>::iterator it = World::Instance().getAvatars().begin(); it != World::Instance().getAvatars().end(); ++it ) {
      if ( it->second != this && it->second->mode() == MODE_REDIT && it->second->room() == room ) {
        send( "Sorry, %s is already editing this room.", seeName( ((Creature*)it->second) ).c_str() );
        while ( mode() != MODE_NONE ) {
          exit.execute( this, exit_args );
        }
        return;
      }
    }
  }
  return;
}

const char* Creature::genderName( void ) const {
  switch ( gender() ) {
    case MALE:    return "male";    break;
    case FEMALE:  return "female";  break;
    default:      return "neutral"; break;
  }
}

bool Creature::wear( Object* article, std::string& message, Object*& removed ) {
  int location = getWearloc( article->wearable() );

  // make sure we can wear it...
  if ( location == WEARLOC_ERROR ) {
    message.assign( "You can't wear " ).append( article->identifiers().shortname() ).append( "{x." );
    return false;
  } else if ( article->level() > level() ) {
    message.assign( "You're not powerful enough to wear " ).append( article->identifiers().shortname() ).append( "{x yet." );
    return false;
  }

  // for single-slot items (head, float, etc)
  if ( isSingleWearLoc( article->wearable() ) ) {
    if ( worn( location ) ) {
      if ( !unwear( worn( location ), message ) ) {
        return false;
      }
    }
  // for double-slot items (like ears, fingers, etc)
  } else {
    // check the first slot
    if ( worn( location ) == NULL ) { }
    // check the second slot
    else if ( worn( location+1 ) == NULL ) {
      ++location;
    }
    // try to force the first slot
    else if ( ( unwear( removed = worn( location ), message ) ) ) { }
    // try to force the second slot
    else if ( ( unwear( removed = worn( location+1 ), message ) ) ) {
      ++location;
    }
    // both slots are taken and can't be reclaimed at this time
    else {
      return false;
    }
  }

  // don the article...
  inventory().remove( article );
  equipment().add( article, location );
  setModifications( article );
  return true;
}

bool Creature::unwear( Object* article, std::string& message, bool force ) {
  if ( !force ) {
    if ( article->flag( Object::Flag_Noremove ) ) {
      message.assign( "You can't remove " ).append( article->identifiers().shortname().c_str() ).append( "{x." );
      return false;
    }
  }
  for ( std::map<int,Object*>::iterator it = equipment().objectMap().begin(); it != equipment().objectMap().end(); ++it ) {
    if ( it->second == article ) {
      equipment().remove( article );
      inventory().add( article );
      unsetModifications( article );
      return true;
    }
  }
  message.assign( "can't find it" );
  return false;
}

Object* Creature::worn( const int& location ) const {
  std::map<int,Object*>::const_iterator it;
  if ( ( it = equipment().objectMap().find( location ) ) != equipment().objectMap().end() ) {
    return it->second;
  }
  return NULL;
}

bool Creature::isSingleWearLoc( const unsigned short& object_weartype ) {
  switch ( object_weartype ) {
    case Object::Wearable_Head:
    case Object::Wearable_Face:
    case Object::Wearable_Neck:
    case Object::Wearable_Shoulders:
    case Object::Wearable_Arms:
    case Object::Wearable_Torso:
    case Object::Wearable_Hands:
    case Object::Wearable_Legs:
    case Object::Wearable_Feet:
      return true;
    default:
      return false;
  }
}

bool Creature::sleep( std::string& error ) {
  if ( isSleeping() ) {
    error.assign( "You're already sleeping." );
    return false;
  }
  if ( !isLaying() && !isSitting() ) {
    error.assign( "You can't sleep while " ).append( positionToString() ).append( "." );
    return false;
  }
  position( Position_Sleeping );
  return true;
}

bool Creature::lay( std::string& error, ObjFurniture* furniture ) {
  if ( isLaying() ) {
    error.assign( "You're already laying down." );
    return false;
  }
  if ( !isSitting() && !isStanding() ) {
    error.assign( "You can't lie down while " ).append( positionToString() ).append( "." );
    return false;
  }
  if ( furniture ) {
    if ( furniture->add( this, ObjFurniture::Position_LayOn ) ) {
      this->furniture( furniture );
      position( Position_Laying );
      return true;
    } else {
      error.assign( "You can't lay on that." );
      return false;
    }
  }
  position( Position_Laying );
  return true;
}

bool Creature::sit( std::string& error, ObjFurniture* furniture, bool on ) {
  ObjFurniture::Position pos = on? ObjFurniture::Position_SitOn : ObjFurniture::Position_SitAt;
  if ( isSitting() ) {
    error.assign( "You're already sitting." );
    return false;
  }
  if ( !isLaying() && !isStanding() ) {
    error.assign( "You can't sit while " ).append( positionToString() ).append( "." );
    return false;
  }
  if ( furniture ) {
    if ( furniture->add( this, pos ) ) {
      this->furniture( furniture );
      position( Position_Sitting );
      return true;
    } else {
      if ( on ) {
        error.assign( "You can't sit on that." );
      } else {
        error.assign( "You can't sit there." );
      }
      return false;
    }
  }
  position( Position_Sitting );
  return true;
}

bool Creature::stand( std::string& error ) {
  if ( isStanding() ) {
    error.assign( "You're already standing." );
    return false;
  }
  if ( isMounted() ) {
    error.assign( "You can't do that while " ).append( positionToString() ).append( "." );
    return false;
  }
  if ( furniture() ) {
    furniture()->remove( this );
    furniture( NULL );
  }
  position( Position_Standing );
  return true;
}

unsigned short Creature::stringToAttribute( const std::string& name ) {
  if ( Regex::strPrefix( name, "hp" ) ) {
    return ATTR_MAX_HP;
  } else if ( Regex::strPrefix( name, "mana" ) ) {
    return ATTR_MAX_MANA;
  } else if ( Regex::strPrefix( name, "move" ) ) {
    return ATTR_MAX_MOVE;
  } else if ( Regex::strPrefix( name, "str" ) ) {
    return ATTR_STR;
  } else if ( Regex::strPrefix( name, "dex" ) ) {
    return ATTR_DEX;
  } else if ( Regex::strPrefix( name, "con" ) ) {
    return ATTR_CON;
  } else if ( Regex::strPrefix( name, "int" ) ) {
    return ATTR_INT;
  } else if ( Regex::strPrefix( name, "wis" ) ) {
    return ATTR_WIS;
  } else if ( Regex::strPrefix( name, "cha" ) ) {
    return ATTR_CHA;
  } else if ( Regex::strPrefix( name, "hr" ) ) {
    return ATTR_HR;
  } else if ( Regex::strPrefix( name, "dr" ) ) {
    return ATTR_DR;
  } else if ( Regex::strPrefix( name, "saves" ) ) {
    return ATTR_SAVES;
  } else if ( Regex::strPrefix( name, "ac" ) ) {
    return ATTR_AC;
  } else if ( Regex::strPrefix( name, "bash" ) ) {
    return ATTR_BASH;
  } else if ( Regex::strPrefix( name, "slash" ) ) {
    return ATTR_SLASH;
  } else if ( Regex::strPrefix( name, "pierce" ) ) {
    return ATTR_PIERCE;
  } else if ( Regex::strPrefix( name, "exotic" ) ) {
    return ATTR_EXOTIC;
  } else {
    return ATTR_END;
  }
}

const char* Creature::attributeToString( const unsigned short& index ) {
  switch ( index ) {
    case ATTR_MAX_HP:   return "hp";
    case ATTR_MAX_MANA: return "mana";
    case ATTR_MAX_MOVE: return "move";
    case ATTR_STR:      return "str";
    case ATTR_DEX:      return "dex";
    case ATTR_CON:      return "con";
    case ATTR_INT:      return "int";
    case ATTR_WIS:      return "wis";
    case ATTR_CHA:      return "cha";
    case ATTR_HR:       return "hr";
    case ATTR_DR:       return "dr";
    case ATTR_SAVES:    return "saves";
    case ATTR_AC:       return "ac";
    case ATTR_BASH:     return "bash";
    case ATTR_SLASH:    return "slash";
    case ATTR_PIERCE:   return "pierce";
    case ATTR_EXOTIC:   return "exotic";
    default:            return "[error]";
  }
}

const char* Creature::positionToString( void ) const {
  switch ( position() ) {
    case Position_Sleeping: return "sleeping";
    case Position_Laying:   return "laying";
    case Position_Sitting:  return "sitting";
    case Position_Standing: return "standing";
    case Position_Mounted:  return "mounted";
    case Position_Fighting: return "fighting";
    default:                return "undefined";
  }
}

void Creature::setModifications( Object* object ) {
  for ( std::list<Modifier*>::const_iterator it = object->modifiers().begin(); it != object->modifiers().end(); ++it ) {
    modify( *it );
  }
  return;
}

void Creature::unsetModifications( Object* object ) {
  for ( std::list<Modifier*>::const_iterator it = object->modifiers().begin(); it != object->modifiers().end(); ++it ) {
    unmodify( *it );
  }
  return;
}

void Creature::modify( Modifier* modifier ) {
  doModification( modifier->attribute(), modifier->magnitude() );
  modifiers().push_back( modifier );
  return;
}

void Creature::unmodify( Modifier* modifier ) {
  doModification( modifier->attribute(), -(modifier->magnitude()) );
  modifiers().remove( modifier );
  return;
}

void Creature::doModification( const unsigned short& attribute, const int& magnitude ) {
  switch ( attribute ) {
    case ATTR_MAX_HP:   _maxHp        += magnitude; break;
    case ATTR_MAX_MANA: _maxMana      += magnitude; break;
    case ATTR_MAX_MOVE: _maxMovement  += magnitude; break;
    case ATTR_STR:      _strength     += magnitude; break;
    case ATTR_DEX:      _dexterity    += magnitude; break;
    case ATTR_CON:      _constitution += magnitude; break;
    case ATTR_INT:      _intelligence += magnitude; break;
    case ATTR_WIS:      _wisdom       += magnitude; break;
    case ATTR_CHA:      _charisma     += magnitude; break;
    case ATTR_HR:       _hitroll      += magnitude; break;
    case ATTR_DR:       _damroll      += magnitude; break;
    case ATTR_SAVES:    _saves        += magnitude; break;
    case ATTR_AC:       _ac           += magnitude; break;
    case ATTR_BASH:     _bash         += magnitude; break;
    case ATTR_SLASH:    _slash        += magnitude; break;
    case ATTR_PIERCE:   _pierce       += magnitude; break;
    case ATTR_EXOTIC:   _exotic       += magnitude; break;
    default: break;
  }
  return;
}

unsigned short Creature::canSee( Creature* target ) {

  // Check for immortal invisibility...
  if ( target->admin( ADMIN_INVIS ) && target->level() > level() ) {
    return SEE_NOTHING;
  }

  return SEE_NAME;
}

std::string Creature::seeName( Creature* target, bool capitalize ) {
  if ( canSee( target ) == SEE_NAME ) {
    return target->identifiers().shortname();
  } else {
    if ( capitalize ) {
      return "Someone";
    } else {
      return "someone";
    }
  }
}

const char* Creature::seeReflexivePronoun( Creature* target, bool capitalize ) {
  switch ( canSee( target ) ) {
    case SEE_NAME:
      switch ( target->gender() ) {
        case MALE:    return capitalize ? "Himself" : "himself";
        case FEMALE:  return capitalize ? "Herself" : "herself";
        default:      return capitalize ? "Itself"  : "itself";
      }
    default:
      return capitalize ? "Themself" : "themself";
  }
}

const char* Creature::seeObjectPronoun( Creature* target, bool capitalize ) {
  switch ( canSee( target ) ) {
    case SEE_NAME:
      switch ( target->gender() ) {
        case MALE:    return capitalize ? "Him" : "him";
        case FEMALE:  return capitalize ? "Her" : "her";
        default:      return capitalize ? "It"  : "it";
      }
    default:
      return capitalize ? "Someone" : "someone";
  }
}

const char* Creature::seePosessivePronoun( Creature* target, bool capitalize ) {
  switch ( canSee( target ) ) {
    case SEE_NAME:
      switch ( target->gender() ) {
        case MALE:    return capitalize ? "His" : "his";
        case FEMALE:  return capitalize ? "Her" : "her";
        default:      return capitalize ? "Its" : "its";
      }
    default:
      return capitalize ? "Someones" : "someones";
  }
}

unsigned short Creature::canSee( Object* target ) {
  return SEE_NAME;
}

std::string Creature::seeName( Object* target, bool capitalize ) {
  if ( canSee( target ) == SEE_NAME ) {
    return target->identifiers().shortname();
  } else {
    if ( capitalize ) {
      return "Something";
    } else {
      return "something";
    }
  }
}

bool Creature::canAlter( Creature* target ) {
  return ( target->level() < level()-2 );
}

bool Creature::canMove( const unsigned short& direction, std::string& message ) {
  Exit* exit = NULL;

  if ( ( exit = room()->exit( direction ) ) == NULL ) {
    message.append( "You cannot go that way." );
    return false;
  }

  if ( exit->flag( EFLAG_CLOSED ) ) {
    message.append( "That door is closed." );
    return false;
  }

  return true;
}

void Creature::move( const unsigned short& direction ) {
  Exit* exit = room()->exit( direction );
  Room* from = room();
  Room* to = exit->target();
  CmdLook look;
  std::vector<std::string> look_args( 1 );

  // Make the switch...
  from->remove( this );
  to->add( this );
  room( to );

  // Send some output...
  send( "You leave %s.\n", Exit::name( direction ) );
  switch ( direction ) {
    case NORTH:
    case EAST:
    case SOUTH:
    case WEST:
      from->send_cond( "$p leaves to the $e.", this, exit );
      to->send_cond( "$p has arrived from the $n.", this, exit );
      break;
    case UP:
      from->send_cond( "$p leaves up.", this, exit );
      to->send_cond( "$p has arrived from below.", this, exit );
      break;
    case DOWN:
      from->send_cond( "$p leaves down.", this, exit );
      to->send_cond( "$p has arrived from above.", this, exit );
      break;
    default:
      break;
  }

  look.execute( this, look_args );
  return;
}
